package ongd.negocio.gestion;

import java.util.ArrayList;
import java.util.List;
import ongd.negocio.sesion.ControladorSesion;
import ongd.servicios.persistencia.ControladorPersistencia;
import ongd.servicios.persistencia.exceptions.IllegalOrphanException;
import ongd.servicios.persistencia.exceptions.NonexistentEntityException;
import ongd.servicios.persistencia.exceptions.PreexistingEntityException;

/**
 * Clase que realizará todas las operaciones referentes a los apadrinamientos
 * en la base de datos..
 *
 * @author <a href="mailto:egi0002@alu.ubu.es">Elena García Iglesias</a>
 * @author <a href="mailto:cms0021@alu.ubu.es">Cristina Martínez Sanz</a>
 * @version 1.0 20120124
 */
public class ControladorApadrinamiento extends ControladorProyecto {

    /**
     * Lista con los apadrinados que vamos a guardar para un proyecto
     */
    private List<Apadrinado> apadrinados;

    /**
     * Constructor de la clase.
     * @param contP controlador persistencia
     * @param contSesion session en la que estamos
     */
    public ControladorApadrinamiento(ControladorPersistencia contP, ControladorSesion contSesion) {
        super(contP, contSesion);
        apadrinados = new ArrayList<Apadrinado>();
    }

    /**
     * Método que crea un proyecto y las personas de contacto o apadrinados 
     * que pertenecen a este.
     * @param proyecto que vamos a crear.
     * @param lista lista d epersonas de contacto que 
     * pertenecen a ese proyecto
     * @return true si el proyecto se ha creado correctamente
     *          false si no se ha creado el proyecto
     * @throws PreexistingEntityException
     * @throws Exception 
     */
    @Override
    public String crearProyecto(Proyecto proyecto, List<PersonaContacto> lista) throws PreexistingEntityException, Exception {
        //Nos aseguramos que el proyecto solo sea de tipo apadrinamiento general o personal
        if (proyecto.esProyectoApadrinamientoGeneral() || proyecto.esProyectoApadrinamientoPersonal()) {
            //Miramos si tienen todos los datos obligatorios.
            if (proyecto.getNombre() != null && proyecto.getPais() != null && proyecto.getResponsableONGD() != null) {
                //Comprobamos que las personas tengan datos
                for (PersonaContacto p : lista) {
                    if (p.getNombre() == null || p.getDireccion() == null || p.getCargo() == null || p.getTelefono1() == null) {
                        return "Incorrecto";
                    }
                }
                //Miramos si ya existe un proyecto con ese nombre, si es asi no creamos el nuevo
                if (buscarProyecto(proyecto.getNombre()) == null) {
                    //Si el proyecto es de apadrinamiento personal y no tiene apadrinados, salta un mensaje
                    if (proyecto.esProyectoApadrinamientoPersonal() && apadrinados.isEmpty()) {
                        return "Falta";
                    }
                    List<Proyecto> listaP = obtenerProyectos();
                    if (listaP.size() > 0) {
                        //El id del nuevo proyecto, será uno mayor que el ultimo proyecto
                        Proyecto proy = listaP.get(listaP.size() - 1);
                        //Introducimos el id del proyecto, uno mas que el tamaño de la lista
                        proyecto.setIdProyecto(proy.getIdProyecto() + 1);
                    } else {
                        //No hay ningun proyecto, por lo que el id es 1
                        proyecto.setIdProyecto(1);
                    }
                    //Inicio la transacción
                    getContP().iniciarTransaccion();
                    //Le ponemos activo
                    proyecto.setActivo(true);
                    getContP().crearEntidad(proyecto);
                    crearPersonasContacto(lista, proyecto);
                    //Solo creamos los apadrinados si es de tipo apadrinamiento personal
                    if (proyecto.esProyectoApadrinamientoPersonal()) {
                        crearListaApadrinados(proyecto);
                    }
                    apadrinados = new ArrayList<Apadrinado>();
                    //Finalizo la transacción
                    getContP().terminarTransaccion();
                    return "Correcto";
                } else {
                    return "Repetido";
                }
            } else {
                return "Incorrecto";
            }
        }
        return "Incorrecto";
    }

    /**
     * Método que borra un proyecto de la base de datos y todo lo relacionado con él.
     * @param proyecto proyecto que vamsoa  borrar.
     * @return true, si el proyecto ha sido borrado correctamente.
     *          false, si el proyecto no ha sido borrado.
     * @throws IllegalOrphanException
     * @throws NonexistentEntityException 
     */
    @Override
    public boolean borrarProyecto(Proyecto proyecto) throws IllegalOrphanException, NonexistentEntityException {
        if(proyecto!=null){
            if(getContP().obtenerEntidad(Proyecto.class,proyecto.getIdProyecto())!=null){
                //Miramos si el proyecto tiene algun padrino que tenga este proyecto como único proyecto.
                for (Persona padrino : proyecto.getPersonaList()) {
                    if (padrino.getProyectoList().size() == 1) {
                        return false;
                    }
                }
                //Inicio la transacción
                getContP().iniciarTransaccion();
                //Borramos los apadrinados
                borrarApadrinados(proyecto.getApadrinadoList());
                //Borramos las personas de contacto
                borrarPersonasContacto(proyecto.getPersonaContactoList());
                //Borramos el proyecto
                getContP().destroyEntidad(Proyecto.class, proyecto.getIdProyecto());
                //Finalizo la transacción
                getContP().terminarTransaccion();
                return true;
            }else{
                return false;
            }
        }else{
            return false;
        }
    }

    /**
     * Método que desactiva un proyecto.
     * @param proyecto que vamsoa  desactivar.
     * @return true, si se ha desactivado correctamente o false si no es así.
     * @throws IllegalOrphanException
     * @throws NonexistentEntityException
     * @throws Exception 
     */
    @Override
    public boolean desactivarProyecto(Proyecto proyecto) throws IllegalOrphanException, NonexistentEntityException, Exception {
        if (getContP().obtenerEntidad(Proyecto.class, proyecto.getIdProyecto()) != null) {
            if(proyecto.getActivo()){
                //Editamos el proyecto desactivandolo.
                proyecto.setActivo(false);
                editarProyecto(proyecto, proyecto.getPersonaContactoList(), false, false);
                return true;
            }else
                return false;
        }
        return false;
    }

    /**
     * Método que guarda un apadrinado de un determinado proyecto.
     * @param apadrinado Apadrinado que vamos a guardar
     * @param proyecto Proyecto al que pertenece el apadrinado
     * @return true si hemos guardado el apadrinado correctamente
     * @throws PreexistingEntityException
     * @throws Exception 
     */
    public boolean guardarApadrinado(Apadrinado apadrinado, Proyecto proyecto) throws PreexistingEntityException, Exception {
        if(apadrinado.getNombre()!=null && getContP().obtenerEntidad(Proyecto.class,proyecto.getIdProyecto())!=null
                && proyecto.esProyectoApadrinamientoPersonal()){
            List<Apadrinado> listaApad = proyecto.getApadrinadoList();
            int i;
            if (listaApad.size() > 0) {
                //El id del nuevo apadrinado, será uno mayor que el ultimo apadrinado
                Apadrinado apad = listaApad.get(listaApad.size() - 1);
                i = apad.getApadrinadoPK().getIdApadrinado() + 1;
            } else {
                i = 1;
            }
            ApadrinadoPK clave = new ApadrinadoPK(i, proyecto.getIdProyecto());
            apadrinado.setApadrinadoPK(clave);
            apadrinado.setProyecto(proyecto);
            getContP().crearEntidad(apadrinado);
            return true;
        }else{
            return false;
        }
    }

    /**
     * Metodo que añade un apadrinado a la lista.
     * @param apadrinado que vamos a guardar. 
     */
    public void guardarApadrinadoLista(Apadrinado apadrinado) {
        if(apadrinado.getNombre()!=null)
            apadrinados.add(apadrinado);
    }

    /**
     * Método que debuelve una lista con todos los apadrinados que hay en la bbdd.
     * @return lista de apadrinados.
     */
    public List<Apadrinado> obtenerApadrinados() {
        return (List<Apadrinado>) getContP().obtenerEntidades(Apadrinado.class);
    }

    /**
     * Método que devuelve la lista de apadrinados que pertenecen a proyectos activos.
     * @return lista de apadrinados.
     */
    public List<Apadrinado> obtenerApadrinadosActivos() {
        List<Apadrinado> apadris = new ArrayList<Apadrinado>();
        for (Apadrinado apadrinado : obtenerApadrinados()) {
            //Si el proyecto al que pertenece es activo, lo añadimos a la lista auxsiliar.
            if (apadrinado.getProyecto().getActivo()) {
                apadris.add(apadrinado);
            }
        }
        return apadris;
    }

    /**
     * Método que edita un apadrinado.
     * @param apadrinado apadrinado que vamos a editar.
     * @return true si la edición se ha realizado correctamente,
     *          false si no se ha editado correctamente.
     * @throws IllegalOrphanException
     * @throws NonexistentEntityException
     * @throws Exception 
     */
    public boolean editarApadrinado(Apadrinado apadrinado) throws IllegalOrphanException, NonexistentEntityException, Exception {
        if (apadrinado.getApadrinadoPK() != null && apadrinado.getNombre()!=null) {
            getContP().editarEntidad(apadrinado);
            return true;
        } else {
            return false;
        }
    }

    /**
     * Método que borra un apadrinado de la base de datos.
     * @param apadrinado apadrinado que vamos a borrar de la base de datos.
     * @param borrarProyecto true si vamos a borrar el proyecto y por lo tanto sus apadrinados.
     *                       false si solo vamos a borrar un apadrinado
     * @return true si ha sido borrado correctamente, false si no lo ha hecho.
     * @throws IllegalOrphanException
     * @throws NonexistentEntityException 
     */
    public boolean borrarApadrinado(Apadrinado apadrinado, boolean borrarProyecto) throws IllegalOrphanException, NonexistentEntityException {
        //Antes de borrar el apadrinado miramos que el proyecto no se quede sin apadrinados
        Proyecto proy = apadrinado.getProyecto();
        if (proy.getApadrinadoList().size() > 1 || borrarProyecto) {
            //Si hay más apadrinados que el que vamos a borrar, le borramos.
            getContP().destroyEntidad(Apadrinado.class, apadrinado.getApadrinadoPK());
            return true;

        } else {
            return false;
        }
    }

    /**
     * Método que borra una lista de apadrinados.
     * @param apadrinados lista de apadrinados que vamos a borrar.
     * @return true si se han borrado todos correctamente o false si no ha sido así.
     * @throws IllegalOrphanException
     * @throws NonexistentEntityException 
     */
    public boolean borrarApadrinados(List<Apadrinado> apadrinados) throws IllegalOrphanException, NonexistentEntityException {
        for (int i = apadrinados.size() - 1; i >= 0; i--) {
            if (!borrarApadrinado(apadrinados.get(i), true)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Método que devuelve una lista con los proyectos que hay en la base 
     * de datos de tipo apadrinamiento.
     * @return lista de proyectos.
     */
    public List<Proyecto> obtenerProyectosApadrinamiento() {
        List<Proyecto> listaProyectos = new ArrayList<Proyecto>();
        listaProyectos.addAll(buscarProyectosTipo("AP"));
        listaProyectos.addAll(buscarProyectosTipo("AG"));
        return listaProyectos;
    }

    /**
     * Método que devuelve una lista con los proyectos que hay en la base 
     * de datos de tipo apadrinamiento que no están activos.
     * @return lista de proyectos.
     */
    public List<Proyecto> obtenerProyectosDesactivadosApadrinamiento() {
        List<Proyecto> listaProyectos = new ArrayList<Proyecto>();
        listaProyectos.addAll(buscarProyectosDesactivadosTipo("AP"));
        listaProyectos.addAll(buscarProyectosDesactivadosTipo("AG"));
        return listaProyectos;
    }

    /**
     * Método que devuelve una lista con los proyectos que hay en la base de 
     * datos de tipo apadrinamiento, teniendo en cuenta que si el proyecto es 
     * de apadrinamiento personal y no tiene apadrinados disponibles.
     * no debe de estar en la lista devuelta
     * @return listaProyectos
     */
    public List<Proyecto> obtenerProyectosApadrinamientoDisponibles() {
        List<Proyecto> listaP = new ArrayList<Proyecto>();
        for (Proyecto p : obtenerProyectosApadrinamiento()) {
            if (p.esProyectoApadrinamientoPersonal()) {
                if (!obtenerApadrinadosDisponibles(p).isEmpty()) {
                    listaP.add(p);
                }
            } else {
                listaP.add(p);
            }
        }
        return listaP;
    }

    /**
     * Método que devuelve una lista con los apadrinados que no disponen
     * de padrino.
     * @param proyecto Proyecto del que queremos obtener los apadrinados.
     * @return lista apadrinados
     */
    public List<Apadrinado> obtenerApadrinadosDisponibles(Proyecto proyecto) {
        List<Apadrinado> listaA = new ArrayList<Apadrinado>();
        if (proyecto.esProyectoApadrinamientoPersonal()) {
            for (Apadrinado a : proyecto.getApadrinadoList()) {
                //Si el apadrinado no está apadrinado
                if (!a.estaApadrinado()) {
                    listaA.add(a);
                }
            }
            //Devuelvo la lista de los apadrinados que tiene ese proyecto.           
            return listaA;
        }
        return listaA;
    }

    /**
     * Método que crea los apadrinados que están guardados en la lista.
     * @param proyecto proyecto al que pertenecen los apadrinados.
     * @throws PreexistingEntityException
     * @throws Exception 
     */
    public void crearListaApadrinados(Proyecto proyecto) throws PreexistingEntityException, Exception {
        if (apadrinados != null) {
            for (Apadrinado apadrinado : apadrinados) {
                guardarApadrinado(apadrinado, proyecto);
            }
        }
    }

    /**
     * Método que me devuelve una lista con los apadrinados uqe hay en común
     * entre el proyecto pasado por parámetro y el padrino.
     * @param proyecto Proyecto
     * @param padrino Padrino
     * @return listaApadrinados
     */
    public List<Apadrinado> obtenerApadrinados(Proyecto proyecto, Persona padrino) {
        if (padrino.esPadrino()) {
            List<Apadrinado> listaTodos = padrino.getApadrinadoList();
            List<Apadrinado> listaApadrinados = new ArrayList<Apadrinado>();
            for (Apadrinado a : listaTodos) {
                if (a.getProyecto().getIdProyecto().compareTo(proyecto.getIdProyecto()) == 0) {
                    //Si el apadrinado pertenece al proyecto, le meto en la lista.
                    listaApadrinados.add(a);
                }
            }
            return listaApadrinados;
        }
        return null;
    }

    /**
     * Método que devuelve la lista de apadrinados.
     * @return lista de apadrinados.
     * @see #setApadrinados
     */
    public List<Apadrinado> getApadrinados() {
        return apadrinados;
    }

    /**
     * Método que asigna una lista de apadrinados a la variable.
     * @param apadrinados nueva lista de apadrinados.
     * @see #getApadrinados
     */
    public void setApadrinados(List<Apadrinado> apadrinados) {
        this.apadrinados = apadrinados;
    }
}
